﻿using CodeGen.TS.Generation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CodeGen.TS.Output
{
    /// <summary>
    /// This class is responsible for creating all typescript code. It is the central handler of all Generators and responsible for exchange between these Generators
    /// </summary>
    public static class TypeGeneratorFactory
    {
        private static IList<ICodeGenerator> registredCodeGenerators = new List<ICodeGenerator>();

        public static ICodeGenerator GetGenerator(Type clrType, bool isObservable)
        {
            foreach (var generator in registredCodeGenerators)
            {
                if (generator.Type == clrType && generator.UseObservable == isObservable)
                    return generator;
            }
            return null;
        }

        public static void RegisterGenerator(ICodeGenerator generator)
        {
            if (!registredCodeGenerators.Contains(generator))
            {
                registredCodeGenerators.Insert(0, generator);//Insert dependent Generators first
            }
        }

        
        private static void PrepareForOutput()
        {
            bool hasOneUnprepared;
            int loopCounter = 0;
            do
            {
                if (loopCounter == 1000)
                {
                    Logger.LogError("Endless Type loop detected. Code will be generated unprepared");
                    return;
                }
                hasOneUnprepared = false;
                ICodeGenerator[] copiedGenerators = new ICodeGenerator[registredCodeGenerators.Count];
                registredCodeGenerators.CopyTo(copiedGenerators, 0);
                foreach (var generator in copiedGenerators)
                {
                    if (!generator.IsPreparedForOutput)
                    {
                        hasOneUnprepared = true;
                        if (generator.RecursionLevel == int.MinValue)
                            generator.RecursionLevel = 0;
                        generator.PrepareForOutput(generator.RecursionLevel==-1?true : loopCounter<generator.RecursionLevel);
                    }
                }
                loopCounter++;
            }
            while (hasOneUnprepared);
        }

        /// <summary>
        /// Writes the code from all generators into the files.
        /// </summary>
        public static void WriteFiles()
        {
            PrepareForOutput();
            string[] files = registredCodeGenerators.Select(s => s.OutputFileName).Distinct().ToArray();
            foreach (string fileName in files)
            {
                var allGenerators = registredCodeGenerators.Where(s => s.OutputFileName == fileName);
                var moduls = allGenerators.Select(s => s.ModulName).Distinct().ToArray();
                string allCode = "";
                bool containsKnockoutCode = false;
                foreach (string modul in moduls)
                {
                    var currentGenerators = allGenerators.Where(s => s.ModulName == modul).ToArray();
                    foreach (var generator in currentGenerators)
                    {
                        containsKnockoutCode = containsKnockoutCode || generator.UseObservable;
                    }

                    if (!string.IsNullOrEmpty(allCode))
                        allCode += Environment.NewLine + Environment.NewLine;
                    allCode += Output.OutputWriter.GenerateCode(currentGenerators, modul);
                }

                Output.OutputWriter.WriteCode(fileName, allCode, containsKnockoutCode);
                Logger.LogMessage("Code for file \"{0}\" generated", fileName);
            }
        }

    }

}
